package dkbta.ontology.elements.abstractions.state;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import dkbta.ontology.Ontology;
import dkbta.ontology.Validatable;
import dkbta.ontology.elements.Element;
import dkbta.ontology.elements.TypedName;
import dkbta.ontology.loading.OntologyException;
import dkbta.ontology.values.ValueCondition;
import dkbta.processing.Patient;

/**
 * An abstract mapping function for states that contains entries per value with value
 * conditions on the abstracted-from elements. Extending classes impose a policy on the
 * amount and kind of simultaneous elements.
 * 
 * @author Uri Kanonov
 */
public abstract class StateMappingFunction implements Validatable{
	/**
	 * The implemented types of state mapping functions
	 * 
	 * @author Uri Kanonov
	 */
	public enum Type{
		AND;
	}

	/** The entries that define the mapping function */
	protected final Collection<Entry> _entries;

	/**
	 * Initializes the state mapping function with its entries
	 * 
	 * @param entries The entries that define the mapping function
	 */
	protected StateMappingFunction(Collection<Entry> entries){
		_entries = entries;
	}

	/**
	 * Returns the type of the mapping function
	 * 
	 * @return The type of the mapping function
	 */
	public abstract Type getType();

	@Override
	public String toString(){
		StringBuilder sb = new StringBuilder();
		sb.append(getType());
		for (Entry e : _entries){
			sb.append("\r\n\t");
			sb.append(e);
		}
		return sb.toString();
	}

	@Override
	public void validate(Ontology ontology) throws OntologyException{
		for (Entry e : _entries){
			e.validate(ontology);
		}
	}

	/**
	 * Goes over all of the instances of the abstracted-from elements obtained from the
	 * patient and attempts to abstract partial or full (depending on the function's type)
	 * occurrences of abstracted-from elements and all necessary contexts
	 * 
	 * @param p The associated patient
	 * @param states The list to which to add the created states
	 * @param stateName The name of the state that will be created
	 * @param abstractedFrom The names and types of the elements the state is abstracted
	 *        from
	 * @param necessaryContexts The contexts necessary for the abstraction process
	 */
	public abstract void performMapping(Patient p, List<State> states, String stateName,
		TypedName[] abstractedFrom, String[] necessaryContexts);

	/**
	 * A state mapping function entry that contains conditions on the abstracted-from
	 * elements and a return (mapped) value
	 * 
	 * @author Uri Kanonov
	 */
	public class Entry implements Validatable{
		/** The constraints on the relevant elements */
		private final Map<TypedName, ValueCondition> _conditions;

		/** The return value of the entry */
		private final String _returnValue;

		/**
		 * Initializes an entry with the given return value.
		 * 
		 * @param returnValue The entry's return value
		 * @param conditions The conditions on the relevant concepts
		 */
		public Entry(String returnValue, Map<TypedName, ValueCondition> conditions){
			_returnValue = returnValue;
			_conditions = conditions;
		}

		/**
		 * Returns the return value of this entry
		 * 
		 * @return The return value of this entry
		 */
		public String getReturnValue(){
			return _returnValue;
		}

		/**
		 * Returns the number of conditions this entry holds
		 * 
		 * @return The number of conditions this entry holds
		 */
		public int getNumberOfConditionss(){
			return _conditions.size();
		}

		/**
		 * Tests the given element instance against the appropriate condition in the
		 * entry. Returns false if no such constraint exists
		 * 
		 * @param eInst The instance
		 * @return Whether the given instance adheres to it's condition or false if no
		 *         such constraint exists
		 */
		public boolean checkCondition(Element eInst){
			ValueCondition constraint = _conditions.get(eInst);
			if (constraint == null){
				return false;
			}

			return constraint.checkCondition(eInst);
		}

		@Override
		public void validate(Ontology ontology) throws OntologyException{
			for (ValueCondition vc : _conditions.values()){
				vc.validate(ontology);
			}
		}

		@Override
		public String toString(){
			StringBuilder sb = new StringBuilder();
			sb.append("Value:").append(_returnValue);
			for (ValueCondition vc : _conditions.values()){
				sb.append("\r\n\t\t");
				sb.append(vc);
			}
			return sb.toString();
		}

	}
}
